package com.googlecode.hypersphere.xsd;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Attribute;
import org.dom4j.Element;

public class EjbJarElement {

	private List<Attribute> attributes;

	private Map<String, EjbJarElement> children = new HashMap<String, EjbJarElement>();

	private boolean isUnbound = false;

	private String name;

	private EjbJarElement parent;

	@SuppressWarnings("unchecked")
	public EjbJarElement(EjbJarElement ejbParent, Element xmlElement) {
		parent = ejbParent;
		name = xmlElement.getName();
		attributes = new ArrayList<Attribute>(xmlElement.attributes());
		addChildrenFromXmlElement(xmlElement);
	}

	private void addChild(EjbJarElement ejbElement) {
		children.put(ejbElement.getName(), ejbElement);
	}

	private void addChildrenFromXmlElement(Element xmlElement) {
		for (Iterator iter = xmlElement.elementIterator(); iter.hasNext();) {
			Element xmlChild = (Element) iter.next();
			EjbJarElement newEjbElement = new EjbJarElement(this, xmlChild);
			if (hasChild(newEjbElement)) {
				newEjbElement.setUnbound();
			}
			addOrMergeChild(newEjbElement);
		}
	}

	public Iterator<Attribute> attributeIterator() {
		return attributes.iterator();
	}

	public Iterator<EjbJarElement> elementIterator() {
		return children.values().iterator();
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final EjbJarElement other = (EjbJarElement) obj;
		return (other.name.equals(this.name) && other.parent
				.equals(this.parent));
	}

	public Collection<EjbJarElement> getChildren() {
		return children.values();
	}

	public String getName() {
		return name;
	}

	private boolean hasChild(EjbJarElement newEjbElement) {
		return (children.values().contains(newEjbElement));
	}

	@Override
	public int hashCode() {
		final int PRIME = 31;
		int result = 1;
		result = PRIME * result + ((name == null) ? 0 : name.hashCode());
		result = PRIME * result + ((parent == null) ? 0 : parent.hashCode());
		return result;
	}

	public boolean isUnbound() {
		return isUnbound;
	}

	private void addOrMergeChild(EjbJarElement otherChild) {
		EjbJarElement myChild = children.get(otherChild.name);
		if (myChild == null) {
			addChild(otherChild);
		} else {
			myChild.merge(otherChild);
		}
	}

	public void merge(EjbJarElement other) {
		if (other.isUnbound()) {
			setUnbound();
		}
		mergeChildren(other);
	}

	private void mergeChildren(EjbJarElement other) {
		for (Iterator<EjbJarElement> iter = other.elementIterator(); iter
				.hasNext();) {
			EjbJarElement otherChild = iter.next();
			addOrMergeChild(otherChild);
		}
	}

	public void setUnbound() {
		isUnbound = true;
	}
}
